#!/bin/bash

# 该脚本在指定文件夹创建Arch软件仓
# 2011-03-29 更新：配合pacman 3.5的数据库
# 2010-06-20 更新：通过 -A 来指定 ARCH

APPNAME=$(basename "${0}")
REPO_DB_FILE_FORMAT="tar.gz"
#REPO_DB_FILE_FORMAT="tar.bz2"
#REPO_DB_FILE_FORMAT="tar.xz"

QUIET=1

usage ()
{
    echo " 用法:  ${APPNAME} -A <$ARCH> <REPO_DIR> <REPO_NAME>"
    echo "      -A <i686, x86_64>  分ARCH来创建"
    echo "      在<REPO_DIR>下创建名为<REPO_NAME>的软件仓"
    exit $1
}

info() {
	echo "      $@"
}
msg() {
	local mesg=$1; shift
	printf "      ==> ${mesg}\n" "$@" >&1
}

msg2() {
	(( QUIET )) && return
	local mesg=$1; shift
	printf "      -> ${mesg}\n" "$@" >&1
}

warning() {
	local mesg=$1; shift
	printf "      ==> WARNING: ${mesg}\n" "$@" >&2
}

error() {
	local mesg=$1; shift
	printf "      ==> ERROR: ${mesg}\n" "$@" >&2
}
# write a list entry
#		arg1 - Entry name
#		arg2 - List
#		arg3 - File to write to
write_list_entry() {
	if [[ -n $2 ]]; then
		echo "%$1%" >>$3
		echo -e $2 >>$3
	fi
}

find_pkgentry()
{
	local pkgname=$1
	local pkgentry
	for pkgentry in $tmpdir/$pkgname*; do
		name=${pkgentry##*/}
		if [[ ${name%-*-*} = $pkgname ]]; then
			echo $pkgentry
			return 0
		fi
	done
	return 1
}

# Get the package name from the delta filename
get_delta_pkgname() {
	local tmp

	tmp=${1##*/}
	echo ${tmp%-*-*_to*}
}


# write a delta entry
#   arg1 - path to delta file
db_write_delta()
{
	deltafile="$1"
	pkgname="$(get_delta_pkgname $deltafile)"

	pkgentry=$(find_pkgentry $pkgname)
	if [[ -z $pkgentry ]]; then
		return 1
	fi
	deltas="$pkgentry/deltas"
	# create deltas file if it does not already exist
	if [[ ! -f $deltas ]]; then
		msg2 "$(gettext "Creating 'deltas' db entry...")"
		echo -e "%DELTAS%" >>$deltas
	fi
	# get md5sum and compressed size of package
	md5sum="$(openssl dgst -md5 "$deltafile")"
	md5sum="${md5sum##* }"
	csize=$(stat -L -c %s "$deltafile")

	oldfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (source)" | sed 's/.*: *//')
	newfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (output)" | sed 's/.*: *//')

	if grep -q "$oldfile.*$newfile" $deltas; then
		warning "$(gettext "An entry for '%s' already existed")" "$deltafile"
		sed -i.backup "/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
		msg2 "$(gettext "Removing existing entry '%s'...")" "$deltafile"
	fi
	echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas

	return 0
} # end db_write_delta

# remove a delta entry
#   arg1 - path to delta file
db_remove_delta()
{
	deltafile="$1"
	filename=${deltafile##*/}
	pkgname="$(get_delta_pkgname $deltafile)"

	pkgentry=$(find_pkgentry $pkgname)
	if [[ -z $pkgentry ]]; then
		return 1
	fi
	deltas="$pkgentry/deltas"
	if [[ ! -f $deltas ]]; then
		return 1
	fi
	if grep -q "$filename" $deltas; then
		sed -i.backup "/$filename/d" $deltas && rm -f $deltas.backup
		msg2 "$(gettext "Removing existing entry '%s'...")" "$filename"
		return 0
	fi

	return 1
} # end db_remove_delta

# write an entry to the pacman database
#   arg1 - path to package  pkg文件绝对路径
db_write_entry()
{
	# blank out all variables
	local pkgfile="$1"
	local pkgname pkgver pkgdesc csize size md5sum url arch builddate packager force \
		_groups _licenses _replaces _depends _conflicts _provides _optdepends

	local OLDIFS="$IFS"
	# IFS (field separator) is only the newline character
	IFS="
"

	# read info from the zipped package
	local line var val
	for line in $(bsdtar -xOqf "$pkgfile" .PKGINFO |
			grep -v '^#' | sed 's|\(\w*\)\s*=\s*\(.*\)|\1 \2|'); do
		# bash awesomeness here- var is always one word, val is everything else
		var=${line%% *}
		val=${line#* }
		declare $var="$val"
		case "$var" in
			group)    _groups="$_groups$group\n" ;;
			license)  _licenses="$_licenses$license\n" ;;
			replaces) _replaces="$_replaces$replaces\n" ;;
			depend)   _depends="$_depends$depend\n" ;;
			conflict) _conflicts="$_conflicts$conflict\n" ;;
			provides) _provides="$_provides$provides\n" ;;
			optdepend) _optdepends="$_optdepends$optdepend\n" ;;
		esac
	done

	IFS=$OLDIFS

	# get md5sum and compressed size of package
	md5sum="$(openssl dgst -md5 "$pkgfile")"
	md5sum="${md5sum##* }"
	csize=$(stat -L -c %s "$pkgfile")

	# ensure $pkgname and $pkgver variables were found
	if [[ -z $pkgname || -z $pkgver ]]; then
		error "Invalid package file '%s'." "$pkgfile"
		return 1
	fi

	cd "$tmpdir"

	msg "添加 $(basename "$1")..."

	# create package directory
	[ -d $pkgname-$pkgver ] && rm -rf $pkgname-$pkgver

	# remove an existing entry if it exists, ignore failures
	db_remove_entry "$pkgname"

	mkdir "$pkgname-$pkgver"
	cd "$pkgname-$pkgver"

	# restore an eventual deltas file
	[[ -f ../$pkgname.deltas ]] && mv "../$pkgname.deltas" deltas

	# create desc entry
	msg2 "提取 'desc' 信息..."
	echo -e "%FILENAME%\n$(basename "$1")\n" >>desc
	echo -e "%NAME%\n$pkgname\n" >>desc
	[[ -n $pkgbase ]] && echo -e "%BASE%\n$pkgbase\n" >>desc
	echo -e "%VERSION%\n$pkgver\n" >>desc
	[[ -n $pkgdesc ]] && echo -e "%DESC%\n$pkgdesc\n" >>desc
	write_list_entry "GROUPS" "$_groups" "desc"
	[[ -n $csize ]] && echo -e "%CSIZE%\n$csize\n" >>desc
	[[ -n $size ]] && echo -e "%ISIZE%\n$size\n" >>desc

	# compute checksums
	msg2 "计算 md5 校验码..."
	echo -e "%MD5SUM%\n$md5sum\n" >>desc

	[[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc
	write_list_entry "LICENSE" "$_licenses" "desc"
	[[ -n $arch ]] && echo -e "%ARCH%\n$arch\n" >>desc
	[[ -n $builddate ]] && echo -e "%BUILDDATE%\n$builddate\n" >>desc
	[[ -n $packager ]] && echo -e "%PACKAGER%\n$packager\n" >>desc
	write_list_entry "REPLACES" "$_replaces" "desc"
	[[ -n $force ]] && echo -e "%FORCE%\n" >>desc

	# create depends entry
	msg2 "提取 'depends' 信息..."
	# create the file even if it will remain empty
	touch "depends"
	write_list_entry "DEPENDS" "$_depends" "depends"
	write_list_entry "CONFLICTS" "$_conflicts" "depends"
	write_list_entry "PROVIDES" "$_provides" "depends"
	write_list_entry "OPTDEPENDS" "$_optdepends" "depends"

	cd "$startdir"

	return 0
} # end db_write_entry

# remove existing entries from the DB
#   arg1 - package name
db_remove_entry() {
	local pkgname=$1
	local notfound=1
	local pkgentry=$(find_pkgentry $pkgname)
	while [[ -n $pkgentry ]]; do
		notfound=0
		if [[ -f $pkgentry/deltas ]]; then
			mv "$pkgentry/deltas" "$tmpdir/$pkgname.deltas"
		fi
		msg2 "$(gettext "Removing existing entry '%s'...")" \
		"$(basename $pkgentry)"
		rm -rf $pkgentry
		pkgentry=$(find_pkgentry $pkgname)
	done
	return $notfound
} # end db_remove_entry


add()
{
	if [[ ! -f $1 ]]; then
		error "$(gettext "File '%s' not found.")" "$1"
		return 1
	fi

	if [[ ${1##*.} == "delta" ]]; then
		deltafile=$1
		msg "$(gettext "Adding delta '%s'")" "$deltafile"
		if ! type xdelta3 &>/dev/null; then
			error "$(gettext "Cannot find the xdelta3 binary! Is xdelta3 installed?")"
			exit 1
		fi
		if db_write_delta "$deltafile"; then
			return 0
		else
			return 1
		fi
	fi

	pkgfile=$1
	if ! bsdtar -tqf "$pkgfile" .PKGINFO >/dev/null 2>&1; then
		error "$(gettext "'%s' is not a package file, skipping")" "$pkgfile"
		return 1
	fi

	msg "$(gettext "Adding package '%s'")" "$pkgfile"

	db_write_entry "$pkgfile"
}

trap_exit()
{
	echo
	error "$@"
	exit 1
}

clean_up() {
	local exit_code=$?

	cd "$startdir"
	[[ -d $tmpdir ]] && rm -rf "$tmpdir"
	(( CLEAN_LOCK )) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"

	exit $exit_code
}

################### Start here
startdir="$PWD"

# 检测、配置输入的命令
while getopts 'A:vh' arg; do
   case "${arg}" in
	A) ARCH="${OPTARG}" ;;
	v) QUIET="n" ;;
        h|?) usage 0 ;;
        *) error "无效参数 '${arg}'"; usage 1 ;;
   esac
done

shift $(($OPTIND - 1))

# 检查重要参数
case "$ARCH" in
	i686) ;;
	x86_64) ;;
	*) error "无效的ARCH $ARCH"; usage 1 ;;
esac

REPO_DIR="${1}"
REPO_NAME="${2}"
count_arch=$(ls -1 ${REPO_DIR}/*-$ARCH.pkg.tar.* | wc -l)
count_any=$(ls -1 ${REPO_DIR}/*-any.pkg.tar.* | wc -l)
count=$((count_arch+count_any))
info "在${REPO_DIR}下创建名为${REPO_NAME}的软件仓"
info "${REPO_DIR}下软件包个数： $count"
info "5妙后继续，Ctrl+C 终止程序"
sleep 5

case "$REPO_DB_FILE_FORMAT" in
	*tar.gz)  TAR_OPT="z" ;;
	*tar.bz2) TAR_OPT="j" ;;
	*tar.xz)  TAR_OPT="J" ;;
esac

REPO_DB_FILE="${REPO_DIR}/${REPO_NAME}.db"

filename=$(basename "$REPO_DB_FILE")

tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX)

for pkg_file in $(ls -1 ${REPO_DIR}/*-$ARCH.pkg.tar.*) $(ls -1 ${REPO_DIR}/*-any.pkg.tar.*); do
	#db_write_entry "${pkg_file}"
	add "${pkg_file}"
done

cd "$tmpdir"
if [[ -n $(ls) ]]; then
	bsdtar -c${TAR_OPT}f "$filename" *
else
	# we have no packages remaining? zip up some emptyness
	warning "$(gettext "No packages remain, creating empty database.")"
	bsdtar -c${TAR_OPT}f "$filename" -T /dev/null
fi
cd "$startdir"

[[ -f ${REPO_DB_FILE}.old ]] && rm -f "${REPO_DB_FILE}.old"
[[ -f $REPO_DB_FILE ]] && mv "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
[[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE" && rm -rf $tmpdir

info "创建完毕！"